# 26. 抽象,接口类、多态、鸭子类型、封装


# 抽象类跟接口类

抽象类跟接口类在python中是同一种意思,但是在别的语言中就不是,比如java,在java中这二个类完全就是二个意思

抽象类跟接口类的定义:强制制定一个规范,凡是继承指定的类中必须要有指定的方法,如果没有,那么在实例化对象的时候会报错

有点抽象,但都可以用实验来验证思路


# 模拟支付 - 不使用抽象跟接口类

在不使用抽象类跟接口类的情况下,制定支付代码规范

class pay:
    def pay(self):
        self.pay()

class Alipay(pay):
    def __init__(self,income):
        self.income = income

    def pay(self):
        print("用户使用支付宝支付了%s元" %self.income)

class wxpay(pay):
    def __init__(self,income):
        self.income = income

    def wxpay(self):
        print("用户使用微信支付了%s元" %self.income)

wx1 = wxpay(100)
al1 = Alipay(10000)
pay.pay(al1)
pay.pay(wx1)

​ 以上代码,如果不使用抽象类跟接口类,那有别人接手这个项目代码,如果他不清楚制定好的规范,那么这就会让微信 支付出现错误,因为在支付调用类中,指定的调用类中方法名是pay名,但是微信支付类的方法名不是按指定的方法 名,所以整个微信支付会报错


# 模拟支付 - 使用抽象跟接口类

使用抽象类跟接口类,强制定制规范,如果不按规范,就会报错


抽象类跟接口类使用需要导入一个模块

from abc import ABCMeta,abstractmethod

模拟项目需要的功能

metaclass=ABCMeta  ##一个类,要让制定规范的父类继承,这样这个类才是抽象类
@abstractmethod   ##强制,放在要强制让子类都必须要用的方法前面,如果子类没有这方法,实例化的时候会报错

报错

from abc import ABCMeta,abstractmethod
class pay(metaclass=ABCMeta):
    @abstractmethod
    def pay(self):
        self.pay()

class Alipay(pay):
    def __init__(self,income):
        self.income = income

    def pay(self):
        print("用户使用支付宝支付了%s元" %self.income)

class wxpay(pay):
    def __init__(self,income):
        self.income = income

    def wxpay(self):
        print("用户使用微信支付了%s元" %self.income)

wx1 = wxpay(100)
al1 = Alipay(10000)

执行结果:
TypeError: Can't instantiate abstract class wxpay with abstract methods pay 

​ 为什么会这样,因为强制要求,只要把pay类作为父类的子类们,都必须要有pay方法,在wxpay类中没有,所以报错


正常

from abc import ABCMeta,abstractmethod
class pay(metaclass=ABCMeta):
    @abstractmethod
    def pay(self):
        self.pay()

class Alipay(pay):
    def __init__(self,income):
        self.income = income

    def pay(self):
        print("用户使用支付宝支付了%s元" %self.income)

class wxpay(pay):
    def __init__(self, income):
        self.income = income

    def pay(self):
        print("用户使用微信支付了%s元" %self.income)

wx1 = wxpay(100)
al1 = Alipay(10000)

​ 以上代码就是按制定好的规范来写的代码,所以不会报错



# 多态 - Python处处都是多态

python面向对象的三大特性之一

在python中无多态,因为python处处都是多态

也是因为python是一门弱类型语言

可能在别的语言中,比如java,java是一门强类型的语言,无论是赋值变量还是实例化传递参时,都是需要先声明这值是什么类型的数据,才能传递

在python中,不管是什么类型的数据,只要能传递,就能传入函数,封装到对象



# 鸭子类型 - 数据整洁概念

鸭子类型:看着像鸭子,这就是鸭子

鸭子就是让多个函数或类中的方法用处一致时,给那些方法起的名字统一

class Str:
    def index(self):
        pass

class List:
    def index(self):
        pass

class Tuple:
    def index(self):
        pass

​ 以上,为什么字符串、列表、无级、都有index方法,因为他们的用处一样,这种类型也被称为鸭子类型



# 封装

封装分为二类:公有封装跟私有封装

公有封装:就是平时实例化对象,给对象空间封装一些属性,也就是公有封装

私有封装:就是私有制

目前主要是说私有封装,公有封装基本都会了


# 私有封装

私有封闭分为:私有静态字段(变量)、私有方法(函数)、私有对象属性

class A:
    name = "这是一个普通的静态字段"
    __name = "这是一个私有的静态字段"

    def __init__(self,name,age):
        self.name = name  ##这是一个普通的对象属性
        self.__age = age  ##这是一个私有的对象属性

    def cat(self):
        print("这是一个普通的方法")

    def __cat(self):
        print("这是一个私有的方法")

# 私有静态字段 (变量)

class A:
    __name = "私有"
    print(__name)

    def so(self):
        print(self.__name)
a1 = A()
a1.so()
print(A.__name)
print(a1.__name)

# 猜这一共能出来几个"私有"
# 我说能出来二个,不信可以自己试试

# 总结

  1. 实例化一下对象不能访问私有字段
  2. 在外部使用类名也不能访问私有字段
  3. 就是说在外部无论使用什么方法都不能访问私有字段,有一个方法除外,下面会说
  4. 私有字段只能在内部使用

# 私有方法 (函数)

class a:
    def __init__(self):
        pass

    def so(self):
        self.__so()

    def __so(self):
        print("这是一个私有的方法")

class b(a):
    def __init__(self):
        self.__so()
a1 = a()
a1.so()
a.__so()
a1.__so()
b1 = b()

# 总结

  1. 在类的外部不能访问私有方法
  2. 在类的子类也不能访问私有方法
  3. 在类的内部可以访问私有方法

# 私有对象属性

class a:
    def __init__(self):
        self.__name = "这是一个私有对象"

    def wo(self):
        print(self.__name)

class b(a):
    def __init__(self):
        print(self.__name)


a1 = a()
a1.wo()
print(a1.__name)
print(a.__init__().__name)
b1 = b()

# 总结

  1. 在类的外部不能访问私有对象
  2. 在类的子类也不能访问私有对象
  3. 在类的内部可以访问私有对象

# 可以使用特殊手段访问私有

可以使用比较特殊的手段来访问私有封装,但是这禁止在工作项目中使用,了解就可

为什么会成为私有,无论是字段、方法、对象,python都是默认在名称前面添加了"_类名"

class A:
    name = "这是一个普通的静态字段"
    __name = "这是一个私有的静态字段"

    def __init__(self,name,age):
        self.name = name  ##这是一个普通的对象属性
        self.__age = age  ##这是一个私有的对象属性

    def cat(self):
        print("这是一个普通的方法")

    def __cat(self):
        print("这是一个私有的方法")

print(A._A__name)
a1 = A("江凡",22)
print(a1._A__age)
A._A__cat(1)
a1._A__cat()